package com.yikome.order.service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.yikome.common.CommonService;
import com.yikome.delivery.dto.GenerateDeliveryOrderSearchOrderDTO;
import com.yikome.delivery.dto.GenerateDeliveryOrderSearchOrderProductDTO;
import com.yikome.delivery.enums.DeliveryStatus;
import com.yikome.delivery.service.DeliveryService;
import com.yikome.order.dto.OrderExportDTO;
import com.yikome.order.dto.OrderProductSearchDTO;
import com.yikome.order.dto.OrderSearchDTO;
import com.yikome.order.entity.Order;
import com.yikome.order.entity.OrderProduct;
import com.yikome.order.enums.OrderStatus;
import com.yikome.order.enums.Payment;
import com.yikome.order.repository.OrderProductRepository;
import com.yikome.order.repository.OrderRepository;
import com.yikome.order.request.OrderEditRequest;
import com.yikome.order.request.OrderProductEditRequest;
import com.yikome.order.request.OrderSearchRequest;
import com.yikome.order.request.OrderToAdminProductRequest;
import com.yikome.order.request.OrderToAdminRequest;
import com.yikome.storehouse.entity.Storehouse;
import com.yikome.storehouse.repository.StorehouseRepository;
import com.yikome.utils.BeanUtils;
import com.yikome.utils.JSONUtils;
import com.yikome.utils.NamedParameterJdbcUtils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;

@Service
public class OrderService extends CommonService<Order, Long> {

	@Value("${shop.remoteurl}")
	private String shopUrl ;
	
	@Autowired
	private OrderRepository orderRepository ; 
	
	@Autowired
	private OrderProductRepository orderProductRepository;
	
	@Autowired
	private StorehouseRepository storehouseRepository;
	
	@Autowired
	private NamedParameterJdbcTemplate jdbcTemplate ;
	
	@Autowired
	private DeliveryService deliveryService ; 
	
	@Autowired
	public void setResourceDao(OrderRepository repository) {
		super.setCommonDao(repository);
	}

	@Transactional
	public Order asyncOrderToAdmin(OrderToAdminRequest orderToAdminRequest) {
		
		Order source = orderRepository.findOrder(orderToAdminRequest.getCode());
		
		if(source != null) return source ;
		
		
		Order order = new Order();
		BeanUtils.copyProperties(orderToAdminRequest, order, "id");
		order.setStatus(OrderStatus.ORDER_STOCK.getStatus());
		order.setCreatedAt(orderToAdminRequest.getCreatedAt());
		if(order.getCreatedAt() == null) {
			order.setCreatedAt(DateUtil.date());
		}
		
		order.setPriority(0);
		
		if(order.getStorehouseId() == null) {
			Storehouse storehouse = storehouseRepository.findDefaultStorehouse();
			if(storehouse != null)order.setStorehouseId(storehouse.getId());
		}
		
		if(orderToAdminRequest.getIsBlackList()) {
			order.setStatus(OrderStatus.ORDER_HOLD.getStatus());
			order.setRemark(StrUtil.concat(true, order.getRemark() , "~黑名单,挂起!!!"));
		}
		
		orderRepository.save(order);
		
		List<OrderProduct> orderProducts = new ArrayList<>();
		
		List<OrderToAdminProductRequest> orderToAdminProductRequests = orderToAdminRequest.getOrderToAdminProductRequests();
		for (OrderToAdminProductRequest orderToAdminProductRequest : orderToAdminProductRequests) {
			
			OrderProduct orderProduct = new OrderProduct();
			BeanUtils.copyProperties(orderToAdminProductRequest, orderProduct, "id");
			orderProduct.setOrderCode(order.getCode());
			orderProducts.add(orderProduct);
		}
		
		orderProductRepository.saveAll(orderProducts);
		
		//TODO 直接走一次出清仓库的判断
		GenerateDeliveryOrderSearchOrderDTO deliveryOrderSearchOrderDTO = new GenerateDeliveryOrderSearchOrderDTO();
		deliveryOrderSearchOrderDTO.setOrderCode(order.getCode());
		deliveryOrderSearchOrderDTO.setDeliveryPrice(order.getPayment() == Payment.PAYMENT_COD.getCode() ? order.getPrice() : BigDecimal.ZERO);
		deliveryOrderSearchOrderDTO.setCurrency(order.getCurrency());
		deliveryOrderSearchOrderDTO.setRemark(order.getRemark());
		deliveryOrderSearchOrderDTO.setConsignee(order.getConsignee());
		deliveryOrderSearchOrderDTO.setTel(order.getTel());
		deliveryOrderSearchOrderDTO.setContry(order.getContry());
		deliveryOrderSearchOrderDTO.setProvince(order.getProvince());
		deliveryOrderSearchOrderDTO.setCity(order.getCity());
		deliveryOrderSearchOrderDTO.setDistrict(order.getDistrict());
		deliveryOrderSearchOrderDTO.setDetails(order.getDetails());
		deliveryOrderSearchOrderDTO.setEmail(order.getEmail());
		deliveryOrderSearchOrderDTO.setZipCode(order.getZipCode());
		deliveryOrderSearchOrderDTO.setStorehouseId(order.getStorehouseId());
		
		List<GenerateDeliveryOrderSearchOrderProductDTO> products = new ArrayList<>();
		for (OrderProduct orderProduct : orderProducts) {
			GenerateDeliveryOrderSearchOrderProductDTO deliveryOrderSearchOrderProductDTO  = new GenerateDeliveryOrderSearchOrderProductDTO();
			deliveryOrderSearchOrderProductDTO.setProductId(orderProduct.getProductId());
			deliveryOrderSearchOrderProductDTO.setProductTitle(orderProduct.getProductTitle());
			deliveryOrderSearchOrderProductDTO.setProductSkuBarcode(orderProduct.getProductSkuBarcode());
			deliveryOrderSearchOrderProductDTO.setProductSkuName(orderProduct.getProductSkuName());
			deliveryOrderSearchOrderProductDTO.setProductPrice(orderProduct.getProductPrice());
			deliveryOrderSearchOrderProductDTO.setProductImageUrl(orderProduct.getProductImageUrl());
			deliveryOrderSearchOrderProductDTO.setQuantity(orderProduct.getQuantity());
			products.add(deliveryOrderSearchOrderProductDTO);
		}
		
		deliveryOrderSearchOrderDTO.setProducts(products);
		deliveryService.checkCreateDelivery(deliveryOrderSearchOrderDTO);
		return order ; 
	}

	
	public Page<OrderSearchDTO> findOrderSearchDTO(OrderSearchRequest orderSearchRequest, Pageable pageable) {
		
		String contenSQL =  "SELECT DISTINCT \n" + 
							"	o_order.id, o_order.`code`, o_order.payment, o_order.price, o_order.freight, o_order.remark, o_order.`status`, o_order.currency, o_order.priority, o_order.created_at, \n" + 
							"	o_order.consignee, o_order.tel, CONCAT_WS(' ',o_order.city, o_order.district, o_order.details) address,  \n" + 
							"	o_order.delivery_code, s_storehouse.storehouse_name \n" + 
							"FROM o_order \n" + 
							"	LEFT JOIN s_storehouse ON (o_order.storehouse_id = s_storehouse.id AND s_storehouse.yn = 1) \n" + 
							"	LEFT JOIN d_delivery ON (o_order.delivery_code = d_delivery.delivery_code AND d_delivery.yn = 1) \n" ; 
		StringBuilder leftJoinSQL = new StringBuilder();
		StringBuilder paramsSQL = new StringBuilder("WHERE 1 = 1 AND o_order.yn = 1 \n");
		Map<String , Object> paramsMap = new HashMap<>();
		
		if(orderSearchRequest.getPayment() != null) {
			paramsSQL.append("AND o_order.payment = :payment \n");
			paramsMap.put("payment", orderSearchRequest.getPayment());
		}
		if(StrUtil.isNotBlank(orderSearchRequest.getRemark())) {
			paramsSQL.append("AND o_order.remark LIKE :remark \n");
			paramsMap.put("remark", StrUtil.wrap(orderSearchRequest.getRemark().trim(), "%"));
		}
		
		if(StrUtil.isNotBlank(orderSearchRequest.getSource())) {
			paramsSQL.append("AND o_order.source = :source \n");
			paramsMap.put("source", orderSearchRequest.getSource());
		}
		
		if(orderSearchRequest.getStatus() != null) {
			paramsSQL.append("AND o_order.`status` = :orderStatus \n");
			paramsMap.put("orderStatus", orderSearchRequest.getStatus());
		}

        if(StrUtil.isNotBlank(orderSearchRequest.getCode())) {
            List<String> codes = StrUtil.split(orderSearchRequest.getCode().replaceAll("\\s", ","), ',');
            if(codes.size() == 1) {
                paramsSQL.append("AND o_order.`code` = :orderCode \n");
                paramsMap.put("orderCode", orderSearchRequest.getCode());
            }else {
                paramsSQL.append("AND o_order.`code` in (:orderCodes) \n");
                paramsMap.put("orderCodes", codes);
            }
        }
        if(StrUtil.isNotBlank(orderSearchRequest.getConsignee())) {
            List<String> consignees = StrUtil.split(orderSearchRequest.getConsignee().replaceAll("\\s", ","), ',');
            if(consignees.size() == 1) {
                paramsSQL.append("AND o_order.consignee = :consignee \n");
                paramsMap.put("consignee", orderSearchRequest.getConsignee());
            }else {
                paramsSQL.append("AND o_order.consignee in (:consignees) \n");
                paramsMap.put("consignees", consignees);
            }
        }
        if(StrUtil.isNotBlank(orderSearchRequest.getTel())) {
            List<String> tels = StrUtil.split(orderSearchRequest.getTel().replaceAll("\\s", ","), ',');
            if(tels.size() == 1) {
                paramsSQL.append("AND o_order.tel = :tel \n");
                paramsMap.put("tel", orderSearchRequest.getTel());
            }else {
                paramsSQL.append("AND o_order.tel in (:tels) \n");
                paramsMap.put("tels", tels);
            }
        }
        if(StrUtil.isNotBlank(orderSearchRequest.getDeliveryCode())) {
            List<String> deliveryCodes = StrUtil.split(orderSearchRequest.getDeliveryCode().replaceAll("\\s", ","), ',');
            if(deliveryCodes.size() == 1) {
                paramsSQL.append("AND o_order.delivery_code = :deliveryCode \n");
                paramsMap.put("deliveryCode", orderSearchRequest.getDeliveryCode());
            }else {
                paramsSQL.append("AND o_order.delivery_code in (:deliveryCodes) \n");
                paramsMap.put("deliveryCodes", deliveryCodes);
            }
        }
		
		if(orderSearchRequest.getStorehouseId() != null) {
			paramsSQL.append("AND o_order.storehouse_id = :storehouseId \n");
			paramsMap.put("storehouseId", orderSearchRequest.getStorehouseId());
		}
		if(StrUtil.isNotBlank(orderSearchRequest.getProductId()) || StrUtil.isNotBlank(orderSearchRequest.getProductSkuBarcode())) {
			leftJoinSQL.append("	LEFT JOIN o_order_product ON (o_order.`code` = o_order_product.order_code AND o_order_product.yn = 1) \n");
			if(StrUtil.isNotBlank(orderSearchRequest.getProductId())) {

                List<String> productIds = StrUtil.split(orderSearchRequest.getProductId().replaceAll("\\s", ","), ',');
                if(productIds.size() == 1) {
                    paramsSQL.append("AND o_order_product.product_id = :productId \n");
                    paramsMap.put("productId", orderSearchRequest.getProductId());
                }else {
                    paramsSQL.append("AND o_order_product.product_id IN (:productIds) \n");
                    paramsMap.put("productIds", productIds);
                }
				
			}
			if(StrUtil.isNotBlank(orderSearchRequest.getProductSkuBarcode())) {
				paramsSQL.append("AND o_order_product.product_sku_barcode = :productSkuBarcode \n");
				paramsMap.put("productSkuBarcode", orderSearchRequest.getProductSkuBarcode().trim());
			}
		}
		if(orderSearchRequest.getBegainTime() != null && orderSearchRequest.getEndTime() != null) {
			paramsSQL.append("AND o_order.created_at BETWEEN FROM_UNIXTIME(:begainTime) AND FROM_UNIXTIME(:endTime) \n");
			paramsMap.put("begainTime", orderSearchRequest.getBegainTime());
			paramsMap.put("endTime", orderSearchRequest.getEndTime());
		}
		
		String orderSQL = NamedParameterJdbcUtils.findOrderSQL(pageable);
		String pageSQL = NamedParameterJdbcUtils.findPageSQL(pageable);
		
		List<OrderSearchDTO> orderSearchDTOs = jdbcTemplate.query(contenSQL + leftJoinSQL + paramsSQL + orderSQL + pageSQL, paramsMap, new BeanPropertyRowMapper<>(OrderSearchDTO.class));
		
		for (OrderSearchDTO orderSearchDTO : orderSearchDTOs) {
			orderSearchDTO.setStatusName(OrderStatus.OrderStatusByStatus(orderSearchDTO.getStatus()).getStatusName());
			orderSearchDTO.setProductSearchDTOs(findOrderProductSearchDTO(orderSearchDTO.getCode()));
		}
		
		Long total = jdbcTemplate.queryForObject("SELECT COUNT(1) FROM (" + contenSQL + leftJoinSQL + paramsSQL + ") T", paramsMap, Long.class);
		
		return new PageImpl<>(orderSearchDTOs, pageable, total);
	}
	
	public List<OrderExportDTO> findExportOrderSearchDTO(OrderSearchRequest orderSearchRequest, Pageable pageable) {
		
		String contenSQL =  "SELECT \n" + 
							"	o_order.`code`, o_order.payment, o_order.price, o_order.freight, o_order.remark, o_order.`status`, o_order.currency, o_order.priority, o_order.created_at, \n" + 
							"	o_order_product.product_id, o_order_product.product_title, o_order_product.product_sku_id, o_order_product.product_sku_barcode, o_order_product.product_sku_name, o_order_product.product_price, o_order_product.product_image_url, o_order_product.quantity," +
							"	o_order_product.source_url, o_order_product.source_number, o_order_product.supplier_name,o_order_product.reference_purchase_price," +
							"	o_order.consignee, o_order.tel, o_order.contry, o_order.city, o_order.district, o_order.details,  \n" + 
							"	o_order.delivery_code, s_storehouse.storehouse_name \n" + 
							"FROM o_order \n" + 
							"	LEFT JOIN o_order_product ON (o_order.`code` = o_order_product.order_code AND o_order_product.yn = 1) \n" + 
							"	LEFT JOIN s_storehouse ON (o_order.storehouse_id = s_storehouse.id AND s_storehouse.yn = 1) \n" + 
							"	LEFT JOIN d_delivery ON (o_order.delivery_code = d_delivery.delivery_code AND d_delivery.yn = 1) \n" ; 
		
		StringBuilder paramsSQL = new StringBuilder("WHERE 1 = 1 AND o_order.yn = 1 \n");
		Map<String , Object> paramsMap = new HashMap<>();
		
		if(orderSearchRequest.getPayment() != null) {
			paramsSQL.append("AND o_order.payment = :payment \n");
			paramsMap.put("payment", orderSearchRequest.getPayment());
		}
		if(StrUtil.isNotBlank(orderSearchRequest.getRemark())) {
			paramsSQL.append("AND o_order.remark LIKE :remark \n");
			paramsMap.put("remark", StrUtil.wrap(orderSearchRequest.getRemark().trim(), "%"));
		}
		if(orderSearchRequest.getStatus() != null) {
			paramsSQL.append("AND o_order.`status` = :orderStatus \n");
			paramsMap.put("orderStatus", orderSearchRequest.getStatus());
		}

		if(StrUtil.isNotBlank(orderSearchRequest.getCode())) {
            List<String> codes = StrUtil.split(orderSearchRequest.getCode().replaceAll("\\s", ","), ',');
            if(codes.size() == 1) {
                paramsSQL.append("AND o_order.`code` = :orderCode \n");
                paramsMap.put("orderCode", orderSearchRequest.getCode());
            }else {
                paramsSQL.append("AND o_order.`code` in (:orderCodes) \n");
                paramsMap.put("orderCodes", codes);
            }
        }
        if(StrUtil.isNotBlank(orderSearchRequest.getConsignee())) {
            List<String> consignees = StrUtil.split(orderSearchRequest.getConsignee().replaceAll("\\s", ","), ',');
            if(consignees.size() == 1) {
                paramsSQL.append("AND o_order.consignee = :consignee \n");
                paramsMap.put("consignee", orderSearchRequest.getConsignee());
            }else {
                paramsSQL.append("AND o_order.consignee in (:consignees) \n");
                paramsMap.put("consignees", consignees);
            }
        }
        if(StrUtil.isNotBlank(orderSearchRequest.getTel())) {
            List<String> tels = StrUtil.split(orderSearchRequest.getTel().replaceAll("\\s", ","), ',');
            if(tels.size() == 1) {
                paramsSQL.append("AND o_order.tel = :tel \n");
                paramsMap.put("tel", orderSearchRequest.getTel());
            }else {
                paramsSQL.append("AND o_order.tel in (:tels) \n");
                paramsMap.put("tels", tels);
            }
        }
        if(StrUtil.isNotBlank(orderSearchRequest.getDeliveryCode())) {
            List<String> deliveryCodes = StrUtil.split(orderSearchRequest.getDeliveryCode().replaceAll("\\s", ","), ',');
            if(deliveryCodes.size() == 1) {
                paramsSQL.append("AND o_order.delivery_code = :deliveryCode \n");
                paramsMap.put("deliveryCode", orderSearchRequest.getDeliveryCode());
            }else {
                paramsSQL.append("AND o_order.delivery_code in (:deliveryCodes) \n");
                paramsMap.put("deliveryCodes", deliveryCodes);
            }
        }
		
		if(orderSearchRequest.getStorehouseId() != null) {
			paramsSQL.append("AND o_order.storehouse_id = :storehouseId \n");
			paramsMap.put("storehouseId", orderSearchRequest.getStorehouseId());
		}
		if(orderSearchRequest.getBegainTime() != null && orderSearchRequest.getEndTime() != null) {
			paramsSQL.append("AND o_order.created_at BETWEEN FROM_UNIXTIME(:begainTime) AND FROM_UNIXTIME(:endTime) \n");
			paramsMap.put("begainTime", orderSearchRequest.getBegainTime());
			paramsMap.put("endTime", orderSearchRequest.getEndTime());
		}
		
		String orderSQL = NamedParameterJdbcUtils.findOrderSQL(pageable);
		String pageSQL = NamedParameterJdbcUtils.findPageSQL(pageable);
		
		List<OrderExportDTO> orderExportDTOs = jdbcTemplate.query(contenSQL + paramsSQL + orderSQL + pageSQL, paramsMap, new BeanPropertyRowMapper<>(OrderExportDTO.class));
		
		for (OrderExportDTO orderExportDTO : orderExportDTOs) {
			orderExportDTO.setStatusName(OrderStatus.OrderStatusByStatus(orderExportDTO.getStatus()).getStatusName());
		}
		
		return orderExportDTOs;
	}

	public List<OrderProductSearchDTO> findOrderProductSearchDTO(String orderCode) {
		
		String contentSQL = "SELECT \n" + 
							"	o_order_product.id order_product_id , o_order_product.product_id, o_order_product.product_title, o_order_product.product_sku_barcode, o_order_product.product_sku_name, o_order_product.product_price, o_order_product.product_image_url, o_order_product.quantity,  \n" + 
							"	o_order_product.source_url, o_order_product.source_number, o_order_product.supplier_name, o_order_product.reference_purchase_price \n" + 
							"FROM o_order_product \n" + 
							"WHERE o_order_product.yn = 1 \n" + 
							"AND o_order_product.order_code = :orderCode \n" +
							"ORDER BY o_order_product.product_sku_barcode ASC \n"; 
		
		return jdbcTemplate.query(contentSQL, ImmutableMap.of("orderCode", orderCode), new BeanPropertyRowMapper<>(OrderProductSearchDTO.class));
	}
	
	public List<OrderProductSearchDTO> findOrderProductInventorySearchDTO(String orderCode) {
		
		String contentSQL = "SELECT \n" + 
							"	o_order_product.id order_product_id, \n" + 
							"	o_order_product.product_id, \n" + 
							"	o_order_product.product_title, \n" + 
							"	o_order_product.product_sku_barcode, \n" + 
							"	o_order_product.product_sku_name, \n" + 
							"	o_order_product.product_price, \n" + 
							"	o_order_product.product_image_url, \n" + 
							"	o_order_product.quantity, \n" + 
							"	o_order_product.source_url, \n" + 
							"	o_order_product.source_number, \n" + 
							"	o_order_product.supplier_name, \n" + 
							"	o_order_product.reference_purchase_price, \n" + 
							"	IFNULL(s_inventory.inventory,0) inventory_quantity, \n" + 
							"	IFNULL(( \n" + 
							"			SELECT \n" + 
							"				SUM(d_delivery_details.quantity) \n" + 
							"			FROM d_delivery \n" + 
							"				LEFT JOIN d_delivery_details ON (d_delivery.delivery_code = d_delivery_details.delivery_code AND d_delivery_details.yn = 1) \n" + 
							"			WHERE d_delivery.yn = 1 \n" + 
							"			AND d_delivery.storehouse_id = o_order.storehouse_id \n" + 
							"			AND d_delivery.`status` BETWEEN :deliveryStartStatus AND :deliveryEndStatus \n" + 
							"			AND d_delivery_details.product_sku_barcode = o_order_product.product_sku_barcode \n" + 
							"	),0) pending_delivery_quantity \n" + 
							"FROM o_order \n" + 
							"	LEFT JOIN o_order_product ON (o_order.`code` = o_order_product.order_code AND o_order_product.yn = 1) \n" + 
							"	LEFT JOIN s_inventory ON (s_inventory.storehouse_id = o_order.storehouse_id AND s_inventory.product_sku_barcode = o_order_product.product_sku_barcode AND s_inventory.yn = 1) \n" + 
							"WHERE o_order.yn = 1 \n" + 
							"AND o_order.`code` = :orderCode \n" ; 
		
		ImmutableMap<String, Object> paramMap = ImmutableMap.of("orderCode", orderCode, "deliveryStartStatus", DeliveryStatus.DELIVERY_CREATED.getStatus(), "deliveryEndStatus", DeliveryStatus.DELIVERY_ASYNC.getStatus());
		
		return jdbcTemplate.query(contentSQL, paramMap, new BeanPropertyRowMapper<>(OrderProductSearchDTO.class));
	}
	
	@Transactional
	public Order cancelOrder(String orderCode) {
		Order order = orderRepository.findOrder(orderCode);
		
		Preconditions.checkArgument(order != null, "订单信息不存在");
		Preconditions.checkArgument(order.getStatus() == OrderStatus.ORDER_STOCK.getStatus() || order.getStatus() == OrderStatus.ORDER_HOLD.getStatus(), "订单状态不允许");
		
		order.setStatus(OrderStatus.ORDER_CANCEL.getStatus());
		
		return orderRepository.save(order);
	}
	
	@Transactional
	public Order deleteOrder(String orderCode) {
		Order order = orderRepository.findOrder(orderCode);
		
		Preconditions.checkArgument(order != null, "订单信息不存在");
		Preconditions.checkArgument(order.getStatus() == OrderStatus.ORDER_STOCK.getStatus() || order.getStatus() == OrderStatus.ORDER_HOLD.getStatus()  || order.getStatus() == OrderStatus.ORDER_CANCEL.getStatus(), "订单状态不允许");
		
		order.setYn(0);
		
		orderRepository.save(order);
		
		List<OrderProduct> orderProducts = orderProductRepository.findOrderProduct(orderCode);
		for (OrderProduct orderProduct : orderProducts) {
			orderProduct.setYn(0);
		}
		orderProductRepository.saveAll(orderProducts);
		
		return order ;
	}
	
	
	@Transactional
	public Order holdlOrder(String orderCode) {
		Order order = orderRepository.findOrder(orderCode);
		
		Preconditions.checkArgument(order != null, "订单信息不存在");
		Preconditions.checkArgument(order.getStatus() == OrderStatus.ORDER_STOCK.getStatus(), "订单状态不允许");
		
		order.setStatus(OrderStatus.ORDER_HOLD.getStatus());
		
		return orderRepository.save(order);
	}
	
	@Transactional
	public Order unHoldOrder(String orderCode) {
		Order order = orderRepository.findOrder(orderCode);
		
		Preconditions.checkArgument(order != null, "订单信息不存在");
		Preconditions.checkArgument(order.getStatus() == OrderStatus.ORDER_HOLD.getStatus(), "订单状态不允许");
		
		order.setStatus(OrderStatus.ORDER_STOCK.getStatus());
		
		return orderRepository.save(order);
	}
	
	@Transactional
	public Order editOrder(OrderEditRequest orderEditRequest) {
		
		Order order = orderRepository.findOrder(orderEditRequest.getOrderCode());
		Preconditions.checkArgument(order != null, "订单信息不存在");
		Preconditions.checkArgument(order.getStatus() == OrderStatus.ORDER_STOCK.getStatus() || order.getStatus() == OrderStatus.ORDER_HOLD.getStatus(), "订单状态不允许");
		
		if(orderEditRequest.getPrice() != null) {
			order.setPrice(orderEditRequest.getPrice());
		}
		if(orderEditRequest.getFreight() != null) {
			order.setFreight(orderEditRequest.getFreight());
		}
		if(orderEditRequest.getPriority() != null) {
			order.setPriority(orderEditRequest.getPriority());
		}
		if(StrUtil.isNotBlank(orderEditRequest.getRemark())) {
			order.setRemark(orderEditRequest.getRemark().trim());
		}
		if(StrUtil.isNotBlank(orderEditRequest.getConsignee())) {
			order.setConsignee(orderEditRequest.getConsignee().trim());
		}
		if(StrUtil.isNotBlank(orderEditRequest.getTel())) {
			order.setTel(orderEditRequest.getTel().trim());
		}
		if(StrUtil.isNotBlank(orderEditRequest.getCity())) {
			order.setCity(orderEditRequest.getCity().trim());
		}
		if(StrUtil.isNotBlank(orderEditRequest.getDistrict())) {
			order.setDistrict(orderEditRequest.getDistrict().trim());
		}
		if(StrUtil.isNotBlank(orderEditRequest.getDetails())) {
			order.setDetails(orderEditRequest.getDetails().trim());
		}
		return orderRepository.save(order);
	}
	
	@Transactional
	public List<Order> splitOrder(String orderCode , List<Long> orderProductIds){
		
		Order sourceOrder = orderRepository.findOrder(orderCode);
		
		Preconditions.checkArgument(sourceOrder.getStatus() == OrderStatus.ORDER_STOCK.getStatus() || sourceOrder.getStatus() == OrderStatus.ORDER_HOLD.getStatus(), "状态不允许操作");
		
		List<OrderProduct> sourceOrderProducts = orderProductRepository.findOrderProduct(orderCode);
		
		Preconditions.checkArgument(orderProductIds.size() < sourceOrderProducts.size(), "拆单商品数量不能超过订单商品数量");
		
		List<OrderProduct> firstOrderProducts = new ArrayList<>();
		List<OrderProduct> secondOrderProducts = new ArrayList<>();
		
		BigDecimal sourceOrderProductPrice = BigDecimal.ZERO;
		for (OrderProduct orderProduct : sourceOrderProducts) {
			if(orderProductIds.contains(orderProduct.getId())) {
				firstOrderProducts.add(orderProduct);
			}else {
				secondOrderProducts.add(orderProduct);
			}
			sourceOrderProductPrice = sourceOrderProductPrice.add(orderProduct.getProductPrice().multiply(BigDecimal.valueOf(orderProduct.getQuantity())));
		}
		
		Preconditions.checkArgument(!firstOrderProducts.isEmpty(), "拆单参数不正确");
		Preconditions.checkArgument(!secondOrderProducts.isEmpty(), "拆单参数不正确");
		
		Order firstOrder = new Order();
		BeanUtils.copyProperties(sourceOrder, firstOrder, "id" , "code");
		firstOrder.setCode(sourceOrder.getCode() + "-1");
		firstOrder.setFreight(BigDecimal.ZERO);
		orderRepository.save(firstOrder);
		BigDecimal firstOrderPrice = BigDecimal.ZERO;
		for (OrderProduct orderProduct : firstOrderProducts) {
			OrderProduct tmp = new OrderProduct();
			BeanUtils.copyProperties(orderProduct, tmp, "id" , "orderCode");
			tmp.setOrderCode(firstOrder.getCode());
			orderProductRepository.save(tmp);
			firstOrderPrice = firstOrderPrice.add(tmp.getProductPrice().multiply(BigDecimal.valueOf(tmp.getQuantity())));
		}
		firstOrder.setPrice(firstOrderPrice.add(sourceOrder.getFreight()).setScale(0, BigDecimal.ROUND_DOWN));
		firstOrder.setFreight(sourceOrder.getFreight());
		orderRepository.save(firstOrder);
		
		
		BigDecimal promotionPrice = sourceOrderProductPrice.add(sourceOrder.getFreight()).subtract(sourceOrder.getPrice());
		
		Order secondOrder = new Order();
		BeanUtils.copyProperties(sourceOrder, secondOrder, "id" , "code");
		secondOrder.setCode(sourceOrder.getCode() + "-2");
		secondOrder.setFreight(BigDecimal.ZERO);
		orderRepository.save(secondOrder);
		BigDecimal secondOrderPrice = BigDecimal.ZERO;
		for (OrderProduct orderProduct : secondOrderProducts) {
			OrderProduct tmp = new OrderProduct();
			BeanUtils.copyProperties(orderProduct, tmp, "id" , "orderCode");
			tmp.setOrderCode(secondOrder.getCode());
			orderProductRepository.save(tmp);
			secondOrderPrice = secondOrderPrice.add(tmp.getProductPrice().multiply(BigDecimal.valueOf(tmp.getQuantity())));
		}
		secondOrder.setPrice(secondOrderPrice.subtract(promotionPrice).setScale(0, BigDecimal.ROUND_DOWN));
		orderRepository.save(secondOrder);
		
		sourceOrder.setYn(0);
		for (OrderProduct orderProduct : sourceOrderProducts) {
			orderProduct.setYn(0);
		}
		orderProductRepository.saveAll(secondOrderProducts);
		
		return ImmutableList.of(firstOrder, secondOrder);
	}
	
	@Transactional
	public Order mergeOrder(List<String> orderCodes) {
		
		Set<String> orderKeySet = new HashSet<>();
		List<Order> orderLists = new ArrayList<>();
		
		for (String orderCode : orderCodes) {
			Order order = orderRepository.findOrder(orderCode);
			String orderKey = StrUtil.format("{}-{}-{}-{}-{}-{}", order.getStorehouseId(), order.getConsignee(), order.getTel(), order.getPayment(), order.getStatus(), order.getCurrency());
			orderKeySet.add(orderKey);
			orderLists.add(order);
		}
		Preconditions.checkArgument(orderKeySet.size() == 1, "条件不允许操作...");
		
		Order mergeOrder = null;
		for (Order order : orderLists) {
			if(mergeOrder == null) {
				mergeOrder = new Order();
				BeanUtils.copyProperties(order, mergeOrder , "id" , "code");
				mergeOrder.setCode("MERGE" + LocalDateTime.now(ZoneId.of("+8")).format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")) + RandomUtil.randomInt(10, 99));
			}else {
				mergeOrder.setPrice(mergeOrder.getPrice().add(order.getPrice()));
				mergeOrder.setFreight(mergeOrder.getFreight().add(order.getFreight()));
				mergeOrder.setRemark(StrUtil.isBlank(mergeOrder.getRemark()) ? (StrUtil.isBlank(order.getRemark()) ? "" : order.getRemark()) : (mergeOrder.getRemark() + ";" + order.getRemark()));
			}
			orderRepository.save(mergeOrder);
			
			List<OrderProduct> findOrderProduct = orderProductRepository.findOrderProduct(order.getCode());
			for (OrderProduct orderProduct : findOrderProduct) {
				
				OrderProduct tmp = orderProductRepository.findOrderProduct(mergeOrder.getCode(), orderProduct.getProductSkuBarcode()) ; 
				
				if(tmp == null) {
					tmp = new OrderProduct();
					BeanUtils.copyProperties(orderProduct, tmp , "id" , "orderCode");
					tmp.setOrderCode(mergeOrder.getCode());
				}else {
					tmp.setQuantity(tmp.getQuantity() + orderProduct.getQuantity());
				}
				
				orderProductRepository.save(tmp);
				orderProduct.setYn(0);
				orderProductRepository.save(orderProduct);
			}
			order.setYn(0);
		}
		orderRepository.saveAll(orderLists);
		
		return mergeOrder ; 
	}
	
	@Transactional
	public OrderProduct editOrderProduct(OrderProductEditRequest orderProductEditRequest) {
		
		if(orderProductEditRequest.getProductPrice() != null) {
			Preconditions.checkArgument(orderProductEditRequest.getProductPrice().compareTo(BigDecimal.ZERO) >= 0, "价格信息有误");
		}
		if(orderProductEditRequest.getReferencePurchasePrice() != null) {
			Preconditions.checkArgument(orderProductEditRequest.getReferencePurchasePrice().compareTo(BigDecimal.ZERO) >= 0, "采购价格信息有误");
		}
		if(orderProductEditRequest.getQuantity() != null) {
			Preconditions.checkArgument(orderProductEditRequest.getQuantity() >= 0, "数量信息有误");
		}
		
		
		OrderProduct orderProduct = orderProductRepository.findOrderProduct(orderProductEditRequest.getOrderProductId());
		
		Preconditions.checkArgument(orderProduct != null, "订单商品信息不存在");
		
		Order order = orderRepository.findOrder(orderProduct.getOrderCode());
		Preconditions.checkArgument(order != null, "订单信息不存在");
		Preconditions.checkArgument(order.getStatus() == OrderStatus.ORDER_STOCK.getStatus() || order.getStatus() == OrderStatus.ORDER_HOLD.getStatus(), "订单状态不允许");
		
		
		if(StrUtil.isNotBlank(orderProductEditRequest.getSourceUrl())) {
			orderProduct.setSourceUrl(orderProductEditRequest.getSourceUrl().trim());
		}
		if(StrUtil.isNotBlank(orderProductEditRequest.getSourceNumber())) {
			orderProduct.setSourceNumber(orderProductEditRequest.getSourceNumber().trim());
		}
		if(orderProductEditRequest.getReferencePurchasePrice() != null) {
			orderProduct.setReferencePurchasePrice(orderProductEditRequest.getReferencePurchasePrice());
		}
		
		if(orderProductEditRequest.getProductPrice() != null || orderProductEditRequest.getQuantity() != null) {
			
			BigDecimal sourceProductPrice = orderProduct.getProductPrice().multiply(BigDecimal.valueOf(orderProduct.getQuantity())).setScale(0, BigDecimal.ROUND_DOWN);
			
			if(orderProductEditRequest.getProductPrice() != null) {
				orderProduct.setProductPrice(orderProductEditRequest.getProductPrice());
			}
			if(orderProductEditRequest.getQuantity() != null) {
				orderProduct.setQuantity(orderProductEditRequest.getQuantity());
			}
			BigDecimal nowProductPrice = orderProduct.getProductPrice().multiply(BigDecimal.valueOf(orderProduct.getQuantity())).setScale(0, BigDecimal.ROUND_DOWN);
			
			if(sourceProductPrice.compareTo(nowProductPrice) != 0) {
				
				order.setPrice(order.getPrice().add(nowProductPrice.subtract(sourceProductPrice)).setScale(0, BigDecimal.ROUND_DOWN));
				orderRepository.save(order);
			}
		}
		
		orderProductRepository.save(orderProduct);
		
		
		
		if(StrUtil.isNotBlank(orderProductEditRequest.getSourceUrl())) {
			
			List<OrderProduct> orderProducts = orderProductRepository.findOrderProductWithProductId(orderProduct.getProductId());
			for (OrderProduct tmp : orderProducts) {
				tmp.setSourceUrl(orderProduct.getSourceUrl());
			}
			orderProductRepository.saveAll(orderProducts);
		}
		
		
		
		return orderProduct;
	}
	
	@Transactional
	public boolean deleteOrderProduct(Long orderProductId) {
		
		OrderProduct orderProduct = orderProductRepository.findOrderProduct(orderProductId);
		Preconditions.checkArgument(orderProduct != null, "订单商品信息不存在");
		
		Order order = orderRepository.findOrder(orderProduct.getOrderCode());
		Preconditions.checkArgument(order != null, "订单信息不存在");
		Preconditions.checkArgument(order.getStatus() == OrderStatus.ORDER_STOCK.getStatus() || order.getStatus() == OrderStatus.ORDER_HOLD.getStatus(), "订单状态不允许");
		
		orderProduct.setYn(0);
		orderProductRepository.save(orderProduct);
		
		order.setPrice(order.getPrice().subtract(orderProduct.getProductPrice().multiply(BigDecimal.valueOf(orderProduct.getQuantity()))).setScale(0, BigDecimal.ROUND_DOWN));
		orderRepository.save(order);
		
		return true;
	}

	
	public Order findOrder(String orderCode) {
		return orderRepository.findOrder(orderCode);
	}
	
	public Order findOrderByDeliveryCode(String deliveryCode) {
		return orderRepository.findOrderByDeliveryCode(deliveryCode);
	}
	
	@Transactional
	public Order changeOrderStatus(Order order) {
		orderRepository.save(order);
		
		HttpResponse execute = HttpRequest.put(shopUrl + "/order/api/status").body(JSONUtils.toJsonString(ImmutableMap.of("orderCode", StrUtil.subBefore(order.getCode(), "-", false), "orderStatus", order.getStatus()))).execute();
		Preconditions.checkArgument(execute.isOk(), "前台订单状态修改失败...");
		return order;
	}
}
